package com.wchen.jvm;

import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 指令中心
 */
public class CommandCenter {
    //区域边界设置，执行范围超出时将忽略该操作
    static Integer maxX = 0;
    static Integer maxY = 0;
    static Integer minX = 0;
    static Integer minY = 0;
    private List<RobotVO> robotVOS = new ArrayList<>();

    CommandCenter(String robotCommands){
        startUp(robotCommands);
    }

    public static void main(String[] args){
        String commands =
                "1 1\n" +
                "1 2 N\n" +
                "LMLMLMLMM\n" +
//                "\n" +
//                "\n" +
                "3 3 E\n" +
                "MMRMMRMRRM";

        String errorCommands =
                "15 15\n" +
                "1 2 N\n" +
                "LMLMLMLMM\n" +
                "\n" +
//                "\n" +
                "3 3 E\n" +
                "MMRMMRMRRM";

        new CommandCenter(commands);

    }

    /**
     * 校验指令的完整性
     * @param commands  所有的指令
     * @return
     */
    private Boolean verifyCommands(String commands){
        Pattern pattern = Pattern.compile("[0-9]+\\s[0-9]+\\n([0-9]+\\s[0-9]+\\s[SNEW]\\n[LMR]*[\\n]?)+");
        Matcher matcher = pattern.matcher(commands);
        Boolean matcherFlag = matcher.matches();
        if(matcherFlag){
            return true;
        }else{
            System.err.println("命令不符合规范，校验失败...");
            return false;
        }
    }

    /**
     * 启动指令中心
     * @param robotCommands 所有的指令
     */
    private void startUp(String robotCommands){
        System.out.println("--------指令中心启动ing---------");
        try {
            initRobots(robotCommands);
            System.out.println("--------指令中心初始化完成---------");
            System.out.println("--------指令中心初启动机器人---------");
            executeRobot();
        }catch (Exception e){
            System.out.println("-------指令中心启动异常" + e.getMessage());
        }
    }

    /**
     * 根据指令初始化机器人
     * @param allCommands   所有的指令
     */
    private void initRobots(String allCommands)throws Exception{
        if(!verifyCommands(allCommands)){
            throw new Exception("命令初始化失败...");
        }
        //匹配第一行
        Pattern patternCoordinate = Pattern.compile("[0-9]+\\s[0-9]+");
        Matcher matcher = patternCoordinate.matcher(allCommands);
        matcher.find();
        String coordinateScope = matcher.group();
        System.out.println("坐标范围：" + coordinateScope);
        //初始化最大坐标
        String[] coordinateScopes = coordinateScope.split("\\s");
        maxX = Integer.parseInt(coordinateScopes[0]);
        maxY = Integer.parseInt(coordinateScopes[1]);

        //匹配机器人具体的命令
        Pattern robotPattern = Pattern.compile("[0-9]+\\s[0-9]+\\s[SNEW]\\n[LMR]*");
        matcher = robotPattern.matcher(allCommands);
        Integer robotNo = 1;
        while (matcher.find()){
            String robotCommands = matcher.group();
            RobotVO robotVO = createRobotVO(robotCommands.split("\\n"), robotNo);
            robotVOS.add(robotVO);
            robotNo ++;
        }
    }

    /**
     * 创建机器人
     * @param commands  每个机器人自己的命令数组
     * @param robotNo   机器人的编号
     * @return
     */
    private RobotVO createRobotVO(String [] commands, Integer robotNo){
        RobotVO robotVO = new RobotVO();
        String[] coordinates = commands[0].trim().split(" ");
        //设置机器人的X，Y及面向方向
        robotVO.setX(Integer.parseInt(coordinates[0]));
        robotVO.setY(Integer.parseInt(coordinates[1]));
        robotVO.setDirection(coordinates[2]);
        //机器人执行的指令
        robotVO.setCommand(commands[1]);
        robotVO.setRobotNo("NO." + robotNo);
        return robotVO;
    }


    /**
     * 执行机器人
     */
    private void executeRobot(){
        robotVOS.forEach(robotVO -> {
            System.out.println(robotVO.getRobotInfo());
            System.out.println("    执行过程：");
            robotVO.executeCommand();
            System.out.println(robotVO.locationInfo());
        });
    }

    /**
     * 机器人类
     */
    public class RobotVO{
        /**
         * 机器待执行指令
         */
        private String command;
        /**
         * 机器人编号
         */
        private String robotNo;
        /**
         * x坐标
         */
        private Integer X;
        /**
         * y坐标
         */
        private Integer Y;
        /**
         * 机器面对方向
         */
        private String direction;

        /**
         * 执行指令
         */
        public void executeCommand(){
            String[] commands = this.getCommand().trim().split("");
            for(String command : commands) {
                if (isDirectCommandType(command)) {//方向计算
                    Integer code = Direction.calculateDirectionCode(this.getDirection(), command);
                    //计算当前方向
                    this.setDirection(Direction.calculateDirect(code));
                } else {//xy轴计算
                    //是否是x轴方向
                    Boolean calculateDirectX = true;
                    //方向正负符号
                    Integer signSymbol = 0;
                    Integer code = Direction.getDirectCode(this.direction);
                    if (code == 1 || code == 3) {
                        calculateDirectX = false;
                    }
                    //坐标计算正负
                    signSymbol = Direction.calculateSignSymbol(code);
                    //移动
                    if(calculateDirectX){//x轴方向
                        Integer nowX = this.getX()+ signSymbol * 1;
                        if(nowX > maxX || nowX < minX){
                            System.out.println("*****机器人" + this.getRobotNo() + ", X轴范围溢出，不执行移动。当前位置："
                                    + this.getX() + " " + this.getY() + " " + this.getDirection());
                            continue;
                        }
                        this.setX(nowX);
                    }else{//y轴
                        Integer nowY = this.getY() + signSymbol * 1;
                        if(nowY > maxY || nowY < minY){
                            System.out.println("*****机器人" + this.getRobotNo() + ", Y轴范围溢出，不执行移动。当前位置："
                                    + this.getX() + " " + this.getY() + " " + this.getDirection());
                            continue;
                        }
                        this.setY(nowY);
                    }
                    System.out.println("     x:" + this.X + ", y:" + this.Y + "  " + this.direction);
                }
            }

        }

        /**
         * 判断是否是方向指令（R,L,M）
         * @param command 单个指令
         * @return
         */
        private Boolean isDirectCommandType(String command){
            switch (command){
                case "L":
                    return true;
                case "R":
                    return true;
                default:
                    return false;
            }
        }

        /**
         * 机器人当前位置信息
         * @return
         */
        public String locationInfo(){
            return "最终位置\n    " + this.getX() + " " + this.getY() + " " + this.getDirection();
        }

        public String getCommand() {
            return command;
        }

        public void setCommand(String command) {
            this.command = command;
        }

        public String getRobotNo() {
            return robotNo;
        }

        public void setRobotNo(String robotNo) {
            this.robotNo = robotNo;
        }

        public Integer getX() {
            return X;
        }

        public void setX(Integer x) {
            X = x;
        }

        public Integer getY() {
            return Y;
        }

        public void setY(Integer y) {
            Y = y;
        }

        public String getDirection() {
            return direction;
        }

        public void setDirection(String direction) {
            this.direction = direction;
        }

        /**
         * 机器信息
         * @return
         */
        public String getRobotInfo(){
            return this.robotNo + "\n    " + this.X + " " + this.Y + " " + this.direction + "\n    " + this.command;
        }
    }

    /**
     * 定义位置方向枚举类
     */
    enum Direction{

        SOUTH("S", 3, -1),
        NORTH("N", 1, 1),
        WEST("W", 4, -1),
        EAST("E", 2, 1);

        //位置定义
        private String direct;
        //位置code
        private Integer code;
        //位置所代表的的正负符号
        private Integer signSymbol;


        private Direction(String direct, Integer code, Integer signSymbol) {
            this.direct = direct;
            this.signSymbol = signSymbol;
            this.code = code;
        }

        /**
         * 计算位置所代表的的code
         * @param sourceDirect      当前方向
         * @param operateDirects    方向操作符
         * @return
         */
        public static Integer calculateDirectionCode(String sourceDirect, String ...operateDirects){
            Integer directCode = getDirectCode(sourceDirect);
            for(String operateDirect : operateDirects){
                switch (operateDirect){
                    case "L":
                        directCode --;
                        break;
                    case "R":
                        directCode ++;
                        break;
                }
            }
            Integer modeDirect = directCode % 4;
            return modeDirect == 0 ? 4 : modeDirect;
        }

        /**
         * 计算位置code所代表的方向
         * @param code 方向代表的code值
         * @return
         */
        public static String calculateDirect(Integer code){
            Integer modeCode = code % 4;
            switch (modeCode){
                case 1:
                    return NORTH.getDirect();
                case 2:
                    return EAST.getDirect();
                case 3:
                    return SOUTH.getDirect();
                case 4:
                    return WEST.getDirect();
                default: //0
                    return WEST.getDirect();
            }
        }

        /**
         * 计算位置所代表的的signSymbol
         * @param code  方向代表的code值
         * @return
         */
        public static Integer calculateSignSymbol(Integer code){
            Integer modeCode = code % 4;
            switch (modeCode){
                case 1:
                    return NORTH.getSignSymbol();
                case 2:
                    return EAST.getSignSymbol();
                case 3:
                    return SOUTH.getSignSymbol();
                case 4:
                    return WEST.getSignSymbol();
                default:
                    return WEST.getSignSymbol();
            }
        }

        /**
         * 通过方向取code
         * @param direction 方向
         * @return
         */
        public static Integer getDirectCode(String direction){
            switch (direction){
                case "S":
                    return SOUTH.getCode();
                case "N":
                    return NORTH.getCode();
                case "E":
                    return EAST.getCode();
                case "W":
                    return WEST.getCode();
                default:
                    return NORTH.getCode();
            }
        }

        public String getDirect() {
            return direct;
        }

        public Integer getSignSymbol() {
            return signSymbol;
        }

        public Integer getCode() {
            return code;
        }
    }

}
